/*---------------------------------------------------------------------
 * File name: halport.cpp
 *
 * Copyright (c) <2020-2022>, <ChenLong>
 *
 * All rights reserved.
 *
 * Author: ChenLong
 * Email: worldlong@foxmail.com
 *--------------------------------------------------------------------*/
#include "stm32f30x_gpio.h"
#include "stm32f30x_exti.h"
#include "stm32f30x_rcc.h"
#include "stm32f30x_misc.h"
#include "stm32f30x_syscfg.h"
#include "hal_gpio.h"
#include "hal_config.h"
#if defined(USING_RT_THREAD)
#include "rtthread.h" 
#endif
/*
*/
static void (*irq_hook[16])(void *param) = {nullptr};
static void *irq_hook_param[16]={nullptr};
const static struct {
  uint8_t irqn;
  uint8_t priority;
}nvic_table[]={
  {EXTI0_IRQn, //EXTI0
   NVIC_IRQ_EXTI0_PRIORITY},
  {EXTI1_IRQn, //EXTI1
   NVIC_IRQ_EXTI1_PRIORITY},
  {EXTI2_TS_IRQn, //EXTI2
   NVIC_IRQ_EXTI2_PRIORITY},
  {EXTI3_IRQn, //EXTI3
   NVIC_IRQ_EXTI3_PRIORITY},
  {EXTI4_IRQn, //EXTI4
   NVIC_IRQ_EXTI4_PRIORITY},
  {EXTI9_5_IRQn, //EXTI5~//EXTI9
   NVIC_IRQ_EXTI9_5_PRIORITY},
  {EXTI15_10_IRQn, //EXTI10 ~ EXTI15
   NVIC_IRQ_EXTI15_10_PRIORITY}
};
/*
*/
bool HAL_GPIO::init(uint64_t cfg0)
{ 
  cfg = cfg0;
  uint32_t tmp = GPIO_CFG_EXTRACT(cfg, PORT);
  uint8_t  exti_port_source = 0;
  
  if(tmp == 0) { //No port
    return false;
  }
#if GPIOA_ENABLE
  else if(tmp == GPIO_PORT(A)) {
    port = (uint32_t)GPIOA;
    exti_port_source = EXTI_PortSourceGPIOA;
  }
#endif
#if GPIOB_ENABLE
  else if(tmp == GPIO_PORT(B)) {
    port = (uint32_t)GPIOB;
    exti_port_source = EXTI_PortSourceGPIOB;
  } 
#endif
#if GPIOC_ENABLE
  else if(tmp == GPIO_PORT(C)) {
    port = (uint32_t)GPIOC;
    exti_port_source = EXTI_PortSourceGPIOC;
  }
#endif
#if GPIOD_ENABLE
  else if(tmp == GPIO_PORT(D)) {
    port = (uint32_t)GPIOD;
    exti_port_source = EXTI_PortSourceGPIOD;
  }
#endif
#if GPIOE_ENABLE
  else if(tmp == GPIO_PORT(E)) {
    port = (uint32_t)GPIOE;
    exti_port_source = EXTI_PortSourceGPIOE;
  }
#endif
#if GPIOF_ENABLE
  else if(tmp == GPIO_PORT(F)) {
    port = (uint32_t)GPIOF;
    exti_port_source = EXTI_PortSourceGPIOF;
  } 
#endif
#if GPIOG_ENABLE
  else if(tmp == GPIO_PORT(G)) {
    port = (uint32_t)GPIOG;
    exti_port_source = EXTI_PortSourceGPIOG;
  } 
#endif
#if GPIOH_ENABLE
  else if(tmp == GPIO_PORT(H)) {
    port = (uint32_t)GPIOH;
    exti_port_source = EXTI_PortSourceGPIOH;
  } 
#endif
  else if(port == 0){
    return false;
  }
  
  if(!config(cfg)) {
    return false;
  }
  
  //EXTI NVIC
  EXTI_InitTypeDef  EXTI_InitStructure;
  tmp = GPIO_CFG_EXTRACT(cfg, EXTI);
  if(tmp != GPIO_EXTI(NONE))
  {
    switch(tmp)
    {
    case GPIO_EXTI(INTRT):
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
      break;
    case GPIO_EXTI(INTFT):
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
      break;
    case GPIO_EXTI(INTRFT):
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
      break;
    case GPIO_EXTI(EVTRT):
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; 
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
      break;
    case GPIO_EXTI(EVTFT):
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; 
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
      break;
    case GPIO_EXTI(EVTRFT):
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; 
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
      break;
    default:
      return false;
    }
  
    /* EXTI line */
    SYSCFG_EXTILineConfig(exti_port_source, _pin_number);
    EXTI_InitStructure.EXTI_Line = _pin_number;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
    
    //NVIC config
    NVIC_InitTypeDef   NVIC_InitStructure;
    if(_pin_number < 5) {
      NVIC_InitStructure.NVIC_IRQChannel = nvic_table[_pin_number].irqn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = nvic_table[_pin_number].priority;
    } else if(_pin_number < 10) {
      NVIC_InitStructure.NVIC_IRQChannel = nvic_table[5].irqn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = nvic_table[5].priority;
    } else {
      NVIC_InitStructure.NVIC_IRQChannel = nvic_table[6].irqn;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = nvic_table[6].priority;
    }
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
  }
  
  return true;
}
/*
*/
bool HAL_GPIO::config(uint64_t cfg0)
{
  cfg &= ~GPIO_CFG_MASK;
  cfg |= cfg0 & GPIO_CFG_MASK;
  
  GPIO_InitTypeDef  GPIO_InitStructure;
 
  //pin
  pin = GPIO_CFG_EXTRACT(cfg, PIN);
  GPIO_InitStructure.GPIO_Pin = pin;
  if(pin == 0) {
    return false;
  }
  
  //pin number, just used for only one pin
  for(int i=0; i<16; i++) {
    if((pin >> i) & 0x01) {
      _pin_number = i;
      break;
    }
  }
  
  //type
  uint32_t tmp = GPIO_CFG_EXTRACT(cfg, TYPE);
  if(tmp == GPIO_TYPE(PPU)) {
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  } else if(tmp == GPIO_TYPE(PPD)) {
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
  } else if(tmp == GPIO_TYPE(PPN)) {
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  } else if(tmp == GPIO_TYPE(ODU)) {
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  } else if(tmp == GPIO_TYPE(ODD)) {
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
  } else if(tmp == GPIO_TYPE(ODN)) {
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  } else if(tmp == GPIO_TYPE(INU)) {
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
  } else if(tmp == GPIO_TYPE(IND)) {
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
  } else if(tmp == GPIO_TYPE(INN)) {
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  } else {
    return false;
  }
  
  //speed
  tmp = GPIO_CFG_EXTRACT(cfg, SPEED);
  if(tmp == GPIO_SPEED(SLOW)) {
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  } else if(tmp == GPIO_SPEED(MEDIUM)) {
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
  } else if(tmp == GPIO_SPEED(FAST)) {
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  } else if(tmp == GPIO_SPEED(HIGH)) {
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  } else {
    return false;
  }
    
  //mode
  tmp = GPIO_CFG_EXTRACT(cfg, MODE);
  if(tmp == GPIO_MODE(OUT)) {
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  } else if(tmp == GPIO_MODE(IN)) {
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
  } else if(tmp == GPIO_MODE(AN)) {
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  } else { //AF0 ~ AF15
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_PinAFConfig((GPIO_TypeDef*)port, _pin_number, tmp - 4);
  }
  
  GPIO_Init((GPIO_TypeDef*)port, &GPIO_InitStructure);
  //status
  if(GPIO_InitStructure.GPIO_Mode == GPIO_Mode_OUT) {
    tmp = GPIO_CFG_EXTRACT(cfg, STATUS);
    if(tmp == GPIO_STATUS(SET)) {
      ((GPIO_TypeDef*)port)->ODR |= 0xffff & pin;
    } else {
      ((GPIO_TypeDef*)port)->ODR &= ~(0xffff & pin);
    }
  }
  
  return true;
}
/*
*/
void HAL_GPIO::set_mode(uint64_t cfg)
{
  if(port) {
    //mode
    cfg &= ~(GPIO_MODE_MASK << GPIO_MODE_POS);
    cfg |= cfg & (GPIO_MODE_MASK << GPIO_MODE_POS);
    
    uint32_t tmp = GPIO_CFG_EXTRACT(cfg, MODE);
    if(tmp > 3) {
      tmp = 2;
    }
    uint32_t mask = 3 << _pin_number*2;
    uint32_t moder = ((GPIO_TypeDef*)port)->MODER & (~mask);
    ((GPIO_TypeDef*)port)->MODER = moder | (tmp << _pin_number*2);
  }
}
/*
*/
uint16_t HAL_GPIO::read()
{
  if(port) {
    GPIO_TypeDef *GPIOx = (GPIO_TypeDef*)port;
    return (GPIOx->IDR & pin);
  }
  return 0;
}
/*
*/
void HAL_GPIO::write(uint16_t val)
{
  if(port) {
    GPIO_TypeDef *GPIOx = (GPIO_TypeDef*)port;
    GPIOx->ODR = (GPIOx->ODR & ~pin) | (val & pin);
  }
}
/*
*/
void HAL_GPIO::toggle()
{
  if(port) {
    GPIO_TypeDef *GPIOx = (GPIO_TypeDef*)port;
    GPIOx->ODR = GPIOx->ODR ^ pin;
  }
}

/*
*/
void HAL_GPIO::attach_irq(void(*fun)(void *param), void *param)
{
  irq_hook[_pin_number] = fun;
  irq_hook_param[_pin_number] = param;
}

/*************************************************
              IRQHandler
**************************************************/
#ifdef __cplusplus
 extern "C" {
#endif
/*
*/
void EXTI0_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(EXTI_GetITStatus(EXTI_Line0) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line0); 
    if(irq_hook[0] != nullptr)
    {
      irq_hook[0](irq_hook_param[0]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI1_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif  
  if(EXTI_GetITStatus(EXTI_Line1) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line1); 
    if(irq_hook[1] != nullptr)
    {
      irq_hook[1](irq_hook_param[1]);
    }
  }   
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI2_TS_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(EXTI_GetITStatus(EXTI_Line2) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line2); 
    if(irq_hook[2] != nullptr)
    {
      irq_hook[2](irq_hook_param[2]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI3_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(EXTI_GetITStatus(EXTI_Line3) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line3); 
    if(irq_hook[3] != nullptr)
    {
      irq_hook[3](irq_hook_param[3]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI4_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(EXTI_GetITStatus(EXTI_Line4) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line4); 
    if(irq_hook[4] != nullptr)
    {
      irq_hook[4](irq_hook_param[4]);
    }
  } 
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI9_5_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(EXTI_GetITStatus(EXTI_Line5) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line5); 
    if(irq_hook[5] != nullptr)
    {
      irq_hook[5](irq_hook_param[5]);
    }
  }
  
  if(EXTI_GetITStatus(EXTI_Line6) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line6); 
    if(irq_hook[6] != nullptr)
    {
      irq_hook[6](irq_hook_param[6]);
    }
  }  
  
  if(EXTI_GetITStatus(EXTI_Line7) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line7); 
    if(irq_hook[7] != nullptr)
    {
      irq_hook[7](irq_hook_param[7]);
    }
  }  
  
  if(EXTI_GetITStatus(EXTI_Line8) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line8); 
    if(irq_hook[8] != nullptr)
    {
      irq_hook[8](irq_hook_param[8]);
    }
  }  
  
  if(EXTI_GetITStatus(EXTI_Line9) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line9); 
    if(irq_hook[9] != nullptr)
    {
      irq_hook[9](irq_hook_param[9]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
void EXTI15_10_IRQHandler(void)
{ 
#if defined(USING_RT_THREAD)
  rt_interrupt_enter(); 
#endif
  if(EXTI_GetITStatus(EXTI_Line10) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line10); 
    if(irq_hook[10] != nullptr)
    {
      irq_hook[10](irq_hook_param[10]);
    }
  }
  
  if(EXTI_GetITStatus(EXTI_Line11) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line11); 
    if(irq_hook[11] != nullptr)
    {
      irq_hook[11](irq_hook_param[11]);
    }
  }  

  if(EXTI_GetITStatus(EXTI_Line12) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line12); 
    if(irq_hook[12] != nullptr)
    {
      irq_hook[12](irq_hook_param[12]);
    }
  }  
  
  if(EXTI_GetITStatus(EXTI_Line13) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line13); 
    if(irq_hook[13] != nullptr)
    {
      irq_hook[13](irq_hook_param[13]);
    }
  }  

  if(EXTI_GetITStatus(EXTI_Line14) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line14); 
    if(irq_hook[14] != nullptr)
    {
      irq_hook[14](irq_hook_param[14]);
    }
  }  
  
  if(EXTI_GetITStatus(EXTI_Line15) != RESET)   
  { 
    EXTI_ClearITPendingBit(EXTI_Line15); 
    if(irq_hook[15] != nullptr)
    {
      irq_hook[15](irq_hook_param[15]);
    }
  }  
#if defined(USING_RT_THREAD)
  rt_interrupt_leave(); 
#endif
}
/*
*/
#ifdef __cplusplus
}
#endif




